package main

import (
	"fmt"

	"github.com/go-kratos/kratos/v2/transport/grpc"
	"github.com/google/wire"

	context "context"

	"entgo.io/ent/dialect"
	sql "entgo.io/ent/dialect/sql"
	ent1 "gitee.com/shuokeyun/common/ent"
	"gitee.com/shuokeyun/kratos/server"
	v1 "gitee.com/shuokeyun/merchant-service/api/v1"
	biz "gitee.com/shuokeyun/merchant-service/internal/biz"
	conf "gitee.com/shuokeyun/merchant-service/internal/conf"
	data "gitee.com/shuokeyun/merchant-service/internal/data"
	ent "gitee.com/shuokeyun/merchant-service/internal/data/ent"
	service "gitee.com/shuokeyun/merchant-service/internal/service"
	log "github.com/go-kratos/kratos/v2/log"
	v8 "github.com/go-redis/redis/v8"
	_ "github.com/go-sql-driver/mysql"
)

var providerSet = wire.NewSet(
	newRegisterGRPCServer,
	service.NewMerchantServer,
	biz.NewMerchantUsecase,
	data.NewMerchantRepo,
	data.NewMerchantCacheRepo,
	newRedisClient,
	newConfBootstrap,
	newLogHelper,
	newDBClient,
	service.NewMerchantUserServer,
	biz.NewMerchantUserUsecase,
	data.NewMerchantUserRepo,
	service.NewUserAuthServer,
	biz.NewUserAuthUsecase,
)

func newRegisterGRPCServer(merchantServer v1.MerchantServer, merchantUserServer v1.MerchantUserServer, userAuthServer v1.UserAuthServer) server.WithAfterGRPCServerFunc {
	return func(g *grpc.Server) error {
		v1.RegisterMerchantServer(g, merchantServer)
		v1.RegisterMerchantUserServer(g, merchantUserServer)
		v1.RegisterUserAuthServer(g, userAuthServer)
		return nil
	}
}

func newConfBootstrap(app *server.App) *conf.Bootstrap {
	v, ok := app.GetAppConfigBootstrap().(*conf.Bootstrap)
	if !ok {
		panic("conf error")
	}
	return v
}

func newDBClient(conf *conf.Bootstrap, log *log.Helper) (*ent.Client, func(), error) {
	c := conf.GetData().GetDatabase()
	if c == nil || c.GetSource() == "" {
		return nil, nil, nil
	}
	switch c.Driver {
	case dialect.MySQL:
		drv, err := sql.Open(c.Driver, c.Source)
		if err != nil {
			return nil, nil, err
		}
		db := drv.DB()
		db.SetMaxIdleConns(int(c.MaxIdel))
		db.SetMaxOpenConns(int(c.MaxOpen))
		if c.MaxLifetime.IsValid() {
			db.SetConnMaxLifetime(c.MaxLifetime.AsDuration())
		}
		client := ent.NewClient(ent.Driver(ent1.NewDriver(drv, log)))
		return client, func() {
			if err := client.Close(); err != nil {
				log.Error(err)
			}
		}, nil
	default:
		return nil, nil, fmt.Errorf("unsupported driver: %q", c.Driver)
	}
}

func newLogHelper(logger log.Logger) *log.Helper {
	return log.NewHelper(logger)
}

func newRedisClient(conf *conf.Bootstrap, log *log.Helper) (*v8.Client, func(), error) {
	c := conf.GetData().GetRedis()
	if c == nil || c.GetAddr() == "" {
		return nil, nil, nil
	}
	rd := v8.NewClient(&v8.Options{
		Network:      c.GetNetwork(),
		Addr:         c.GetAddr(),
		Password:     c.GetPassword(),
		DB:           int(c.GetDb()),
		ReadTimeout:  c.GetReadTimeout().AsDuration(),
		WriteTimeout: c.GetWriteTimeout().AsDuration(),
		PoolSize:     int(c.GetPoolSize()),
		IdleTimeout:  c.GetIdleTimeout().AsDuration(),
	})
	if err := rd.Ping(context.Background()).Err(); err != nil {
		return nil, nil, err
	}
	return rd, func() {
		err := rd.Close()
		if err != nil {
			log.Errorf("closing the redis resources fail, err = %s", err.Error())
		} else {
			log.Info("closing the redis resources")
		}
	}, nil
}
